#include <memory>
#include <ctime>
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <pthread.h>
#include <vector>
#include <queue>
#include <unistd.h>


using namespace std;

class Task
{
public:
    Task()
    {
    }
    Task(int x, int y, char op)
            : _x(x), _y(y), _op(op), _result(0), _exitCode(0)
    {
    }
    void operator()()
    {
        switch (_op)
        {
            case '+':
                _result = _x + _y;
                break;
            case '-':
                _result = _x - _y;
                break;
            case '*':
                _result = _x * _y;
                break;
            case '/':
                if (_y == 0)
                    _exitCode = -1;
                else
                    _result = _x / _y;
                break;
            case '%':
                if (_y == 0)
                    _exitCode = -2;
                else
                    _result = _x % _y;
                break;
            default:
                break;
        }
        usleep(100000);
    }

    string formatArg()
    {
        return to_string(_x) + " " + _op + " " + to_string(_y) + " = ";
    }

    string formatRes()
    {
        return to_string(_result) + "(" + to_string(_exitCode) + ")";
    }

    ~Task()
    {
    }

private:
    int _x;
    int _y;
    char _op;
    int _result;
    int _exitCode;
};



class Thread{
public:
    typedef enum{
        NEW = 0,
        RUNNING,
        EXITED
    } ThreadStatus;

    typedef void (*func_t)(void*);

    Thread(int num, func_t func, void *args)
            : _tid(0),
              _status(NEW),
              _func(func),
              _args(args){
        char name[128];
        snprintf(name, sizeof(name), "thread-%d", num);
        _name = name;
    }

    int status(){
        return _status;
    }

    string threadName(){
        return _name;
    }

    pthread_t threadId(){
        if(_status == RUNNING)
            return _tid;
        else
            return 0;
    }

    static void* runHelper(void* args){
        Thread *tp = (Thread*)args;
        if((tp->_func) != nullptr)
            tp->_func(tp->_args);
        return nullptr;
    }

    void run(){
        int n = pthread_create(&_tid, nullptr, runHelper, this);
        if(n != 0)
            exit(1);
        _status = RUNNING;
    }

    void join(){
        int n = pthread_join(_tid, nullptr);
        if( n != 0){
            cerr << "main thread join thread " << _name << " error" << endl;
            return;
        }
        _status = EXITED;
    }

    ~Thread(){

    }

private:
    pthread_t _tid;
    string _name;
    func_t _func;
    void *_args;
    ThreadStatus _status;
};

#pragma once



const static int N = 20;

template <class T>
class ThreadPool{
public:
    ThreadPool(int num = N)
            : _num(num){
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    void lockQueue(){
        pthread_mutex_lock(&_lock);
    }

    void unlockQueue(){
        pthread_mutex_unlock(&_lock);
    }

    void threadWait(){
        pthread_cond_wait(&_cond, &_lock);
    }

    void threadWackup(){
        pthread_cond_signal(&_cond);
    }

    bool isEmpty(){
        return _tasks.empty();
    }

    T popTask(){
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

    static void threadRoutine(void *args){
        //pthread_detach(pthread_self());
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *> (args);
        while(true){
            tp->lockQueue();
            while(tp->isEmpty()){
                tp->threadWait();
            }
            T t = tp->popTask();
            tp->unlockQueue();

            tp->runTask(t);
            //t();
            //cout << pthread_self() << " | consumer data: " << t.formatArg() << t.formatRes() << endl;

        }
    }

    void init(){
        for(int i = 0; i < _num; i++){
            _threads.push_back(Thread(i, threadRoutine, this));
        }
    }

    void start(){
        for(auto &t : _threads)
            t.run();
    }

    void check(){
        for(auto &t : _threads)
            cout << t.threadName()  << " " << t.threadId() << "running" << endl;
    }

    void runTask(T t){
        t();
        cout << pthread_self() << " | consumer data: " << t.formatArg() << t.formatRes() << endl;
    }

    void pushTask(const T& t){
        lockQueue();
        _tasks.push(t);
        threadWackup();//push完任务后将排队等待的线程唤醒
        unlockQueue();
    }

    ~ThreadPool(){
        for(auto &t : _threads)
            t.join();
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }

private:
    vector<Thread> _threads;
    int _num;
    queue<T> _tasks;
    pthread_mutex_t _lock;
    pthread_cond_t _cond;
};


int main(){
    unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>(N));
    tp->init();
    tp->start();
    //tp->check();

    string opers = "+-*/%";

    while(true){
        int x = rand() % 20 + 1;
        int y = rand() % 10 + 1;
        char op = opers[rand() % opers.size()];
        Task t(x, y, op);
        tp->pushTask(t);
    }
}

